package cn.jingyuan.swan.uaa.web.controller;

import cn.jingyuan.bee.spring.utils.WebUtils;
import cn.jingyuan.bee.utils.bean.BeanUtils;
import cn.jingyuan.swan.cloud.core.Result;
import cn.jingyuan.swan.cloud.core.ResultUtils;
import cn.jingyuan.swan.cloud.oauth2.client.DefaultOAuth2ClientDetails;
import cn.jingyuan.swan.cloud.oauth2.client.DefaultOAuth2ClientProperties;
import cn.jingyuan.swan.cloud.web.client.NoErrorResponseErrorHandler;
import cn.jingyuan.swan.uaa.web.api.AccessTokenApi;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.util.Map;

@Api(tags = "Access Token 接口")
@Slf4j
@RestController
public class AccessTokenController implements AccessTokenApi {

    static final String PARAM_GRANT_TYPE = "grantType";

    static final String PARAM_USERNAME = "username";

    static final String PARAM_PASSWORD = "password";

    @Resource
    TokenStore tokenStore;

    @Resource
    DefaultOAuth2ClientProperties oAuth2ClientProperties;

    RestTemplate restTemplate;

    public AccessTokenController() {
        RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
        template.setErrorHandler(new NoErrorResponseErrorHandler());
        this.restTemplate = template;
    }


    @ApiOperation(value = "获取令牌 ", notes = "用户名、用户名和密码获取 token")
    @Override
    public Result<Map<String, Object>> getAccessToken(@RequestBody Map<String, String> params) {
        DefaultOAuth2ClientDetails client = oAuth2ClientProperties.getProvider().get("swan-cloud");
        String grantType = params.get(PARAM_GRANT_TYPE);
        String username = params.get(PARAM_USERNAME);
        String password = params.get(PARAM_PASSWORD);

        MultiValueMap<String, Object> postParameters = new LinkedMultiValueMap<>();
        postParameters.add("client_id", client.getClientId());
        postParameters.add("client_secret", client.getClientSecret());
        postParameters.add("grant_type", grantType);
        postParameters.add(PARAM_USERNAME, username);
        postParameters.add(PARAM_PASSWORD, password);

        Map<String, String> headers = WebUtils.getHeaders();
        HttpHeaders postHeaders = new HttpHeaders();
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            postHeaders.add(entry.getKey(), entry.getValue());
        }

        // 强制移除 原来的请求头，防止 token 失效
        postHeaders.remove(HttpHeaders.AUTHORIZATION);
        // 使用客户端的请求头,发起请求
        postHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        String tokeUrl = WebUtils.getServerUrl() + "/oauth/token";

        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity(postParameters, postHeaders);
        Map<String, Object> tokenMap = restTemplate.postForObject(tokeUrl, request, Map.class);

        Result<Map<String, Object>> rs;
        if (tokenMap.containsKey("access_token")) {
            rs = ResultUtils.ok(tokenMap);
        } else {
            rs = BeanUtils.mapToBean(tokenMap, Result.class, true);
        }

        return rs;
    }


    @ApiOperation(value = "移除令牌 ", notes = "退出登录，移除令牌")
    @Override
    public Result<Void> removeToken(Map<String, String> params) {
        String token = params.get("token");
        if (null != token) {
            OAuth2AccessToken accessToken = tokenStore.readAccessToken(token);
            if (null != accessToken) {
                tokenStore.removeAccessToken(accessToken);
                log.debug("remove token={}", token);
            }
        }
        return ResultUtils.ok();
    }

}
